
/*
    Software License

    Copyright (C) 2021-05-24  Xoronos

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 3.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

/*
    Liabilities

    The software is provided "AS IS" without any warranty of any kind, either expressed,
    implied, or statutory, including, but not limited to, any warranty that the software
    will conform to specifications, any implied warranties of merchantability, fitness
    for a particular purpose, and freedom from infringement, and any warranty that the
    documentation will conform to the software, or any warranty that the software will
    be error free.

    In no event shall Xoronos be liable for any damages, including, but not limited to,
    direct, indirect, special or consequential damages, arising out of, resulting from,
    or in any way connected with this software, whether or not based upon warranty,
    contract, tort, or otherwise, whether or not injury was sustained by persons or
    property or otherwise, and whether or not loss was sustained from, or arose out of
    the results of, or use of, the software or services provided hereunder.

    To request the provided software under a different license you can contact us at
    support@xoronos.com
*/

#include "xrn_core.h"
#include "xrn_core_backend.h"

int xrn_load_header_bytes( uint8_t * in_bytes,
                           uint64_t in_bytes_length,
                           uint8_t * crc_bytes,
                           xrn_lib_xtype_t * type,
                           xrn_lib_xtype_t exp_type,
                           uint8_t * state_size,
                           uint64_t * index,
                           uint32_t * reserved_0,
                           uint32_t * reserved_1,
                           xrn_encoding_param_t * enc_param, xrn_crypto_extra_settings_t settings
 )
{
    XPRINT_DEB(  );

    int i, ret, final_ret;
    int nbits;
    uint16_t crc16;
    uint8_t crc_bytes_local;
    xrn_crypto_lib_header_t header_local;

    xrn_encoding_tmp_param_t enc_param_tmp;
    xrn_crypto_lib_header_t header_local2;

    if ( ( index == NULL ) || ( in_bytes == NULL ) || ( type == NULL ) || ( crc_bytes == NULL )
         || ( enc_param == NULL ) ) {
        XPRINT_ERR( 2315, XERR_NULL_MSG );
        return XERR_NULL;
    }

    if ( in_bytes_length < sizeof( xrn_crypto_lib_header_t ) ) {
        XPRINT_ERR( 1977, XERR_IO_MSG );
        return XERR_IO;
    }

    for ( i = 0; i < sizeof( header_local ); i++ ) {
        ( ( uint8_t * ) & header_local )[i] = 0;
    }

    final_ret = XSUCCESS;

    // initialize encoding decoding param

    ret = xrn_check_encoding_settings( settings.encoding_settings.i_settings );
    final_ret = xrn_store_warnings_in_return_value( ret, final_ret );
    if ( final_ret == XERR_GENERAL ) {
        XPRINT_ERR( 2303, XERR_GENERAL_MSG );
        return ret;
    }

    // initialize encoding decoding param

    ret = xrn_initialize_encoding_parameters( enc_param, settings.encoding_settings.i_settings );
    final_ret = xrn_store_warnings_in_return_value( ret, final_ret );
    if ( final_ret == XERR_GENERAL ) {
        XPRINT_ERR( 72, XERR_GENERAL_MSG );
        return ret;
    }

    if ( in_bytes_length < sizeof( xrn_crypto_lib_header_t ) ) {
        XPRINT_ERR( 2255, XERR_GENERAL_MSG );
        return XERR_BUFF;
    }

    for ( i = 0; i < sizeof( xrn_crypto_lib_header_t ); i++ ) {
        ( ( uint8_t * ) & header_local )[i] = in_bytes[i];

    }

    ret =
        xrn_decode( enc_param, ( uint8_t * ) & header_local, ( uint8_t * ) & header_local,
                    sizeof( header_local ), &enc_param_tmp );

    final_ret = xrn_store_warnings_in_return_value( ret, final_ret );
    if ( final_ret == XERR_GENERAL ) {
        XPRINT_ERR( 833, XERR_GENERAL_MSG );
        return ret;
    }

    // checks on header

    header_local2 = header_local;
    header_local2.crc16 = 0xffff;
    crc16 = 0xffff;
    xrn_crc16( ( uint8_t * ) & header_local2, sizeof( header_local2 ) - 1, &crc16 );
    header_local2.crc16 = crc16;

    if ( header_local2.crc16 != header_local.crc16 ) {
        XPRINT_ERR( 2254, XERR_CRC_MSG );
        return XERR_CRC;
    }

    ret = xrn_compare_version( header_local.version, header_local.reserved_1 );
    final_ret = xrn_store_warnings_in_return_value( ret, final_ret );
    if ( final_ret == XERR_GENERAL ) {
        XPRINT_ERR( 2257, XERR_GENERAL_MSG );
        return ret;
    }

    ret = xrn_check_valid_type( ( uint8_t ) ( header_local.type >> 3 ) );
    final_ret = xrn_store_warnings_in_return_value( ret, final_ret );
    if ( final_ret == XERR_GENERAL ) {
        return ret;
    }

    xrn_load_sub_type( header_local.type, &crc_bytes_local, &nbits );

    if ( header_local.magic_number != XRN_PERMUTATION_NUMBER ) {
        XPRINT_ERR( 253, XERR_MAGIC_MSG );
        return XERR_MAGIC;
    }

    if ( nbits != XNBITS ) {
        XPRINT_ERR( 2260, XERR_NBITS_MSG );
        return XERR_NBITS;
    }

    if ( ( ( uint8_t ) ( header_local.type >> 3 ) ) != exp_type ) {
        XPRINT_ERR( 208, XERR_TYPE_MSG );
        return XERR_TYPE;
    }

    if ( ( header_local.state_size < XRN_MIN_ALLOWED_STATE_SIZE )
         && ( header_local.state_size > ( sizeof( xnum_t ) * 8 ) - 1 ) ) {
        XPRINT_ERR( 610, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    if ( crc_bytes_local == 0 ) {
        crc_bytes_local = 1;
    } else if ( crc_bytes_local == 1 ) {
        crc_bytes_local = 2;
    } else if ( crc_bytes_local == 2 ) {
        crc_bytes_local = 4;
    } else if ( crc_bytes_local == 3 ) {
        crc_bytes_local = 8;
    } else {
        XPRINT_ERR( 1055, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    *type = header_local.type >> 3;
    *crc_bytes = crc_bytes_local;
    *state_size = header_local.state_size;
    *reserved_0 = header_local.reserved_0;
    *reserved_1 = header_local.reserved_1;
    *index = sizeof( header_local );

    return final_ret;

}

int xrn_dump_header_bytes( uint8_t * out_bytes,
                           uint64_t out_bytes_length,
                           xrn_lib_xtype_t type,
                           uint8_t crc_bytes,
                           uint8_t state_size,
                           uint64_t * index,
                           xrn_encoding_param_t * enc_param, xrn_crypto_extra_settings_t settings
 )
{
    XPRINT_DEB(  );

    int i, ret, final_ret;
    xrn_crypto_lib_header_t header;
    xrn_encoding_tmp_param_t enc_tmp;
    uint16_t crc16;

    if ( ( out_bytes == NULL ) || ( enc_param == NULL ) || ( index == NULL ) ) {
        XPRINT_ERR( 2314, XERR_NULL_MSG );
        return XERR_NULL;
    }

    if ( out_bytes_length < sizeof( header ) ) {
        XPRINT_ERR( 197, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    if ( XSUCCESS != xrn_check_encoding_settings( settings.encoding_settings.o_settings ) ) {
        XPRINT_ERR( 2308, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    if ( crc_bytes == 1 ) {
        crc_bytes = 0;
    } else if ( crc_bytes == 2 ) {
        crc_bytes = 1;
    } else if ( crc_bytes == 4 ) {
        crc_bytes = 2;
    } else if ( crc_bytes == 8 ) {
        crc_bytes = 3;
    } else {
        XPRINT_ERR( 1052, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    if ( ( state_size < XRN_MIN_ALLOWED_STATE_SIZE )
         && ( state_size > ( sizeof( xnum_t ) * 8 ) - 1 ) ) {
        XPRINT_ERR( 613, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    final_ret = XSUCCESS;

    // initialize encoding decoding param
    ret = xrn_initialize_encoding_parameters( enc_param, settings.encoding_settings.o_settings );
    final_ret = xrn_store_warnings_in_return_value( ret, final_ret );
    if ( final_ret == XERR_GENERAL ) {
        XPRINT_ERR( 2312, XERR_GENERAL_MSG );
        return ret;
    }

    for ( i = 0; i < sizeof( header ); i++ ) {
        ( ( uint8_t * ) & header )[i] = 0;
    }

    if ( XSUCCESS != get_random_bytes( &header.encoding_guard[0], sizeof( header.encoding_guard ) ) ) {
        XPRINT_ERR( 609, XERR_IO_MSG );
        return XERR_IO;
    }

    header.magic_number = XRN_PERMUTATION_NUMBER;

    header.version = XRN_LIB_VERSION;
    header.reserved_0 = settings.component_id;
    header.reserved_1 = XRN_GIT_VERSION;
    xrn_dump_sub_type( &header.type, crc_bytes, XNBITS );
    header.type = ( uint8_t ) ( ( type << 3 ) | header.type );
    header.state_size = state_size;
    header.crc16 = 0xffff;

    crc16 = 0xffff;
    xrn_crc16( ( uint8_t * ) & header, sizeof( header ) - 1, &crc16 );
    header.crc16 = crc16;

    if ( XSUCCESS != xrn_encode( enc_param, ( uint8_t * ) & header, out_bytes, sizeof( header ), &enc_tmp ) ) {
        XPRINT_ERR( 2310, XERR_IO_MSG );
        return XERR_IO;
    }

    *index = sizeof( header );

    return final_ret;

}

